#ifndef BLOCKQUEUE_H_
#define BLOCKQUEUE_H_

#include <mutex>
#include <condition_variable>
#include <chrono>
#include <stdexcept>

template <class T>
class BlockQueue
{
public:
    explicit BlockQueue(int max_size = 1000)
        : max_size_(max_size), start_(0), end_(0), size_(0)
    {
        array_ = new T[max_size_];
        if (array_ == nullptr)
        {
            throw std::runtime_error("Failed to allocate memory for queue");
        }
    }

    ~BlockQueue()
    {
        delete[] array_;
    }

    void clear()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        start_ = end_ = size_ = 0;
    }

    bool isEmpty() const
    {
        return size_ == 0;
    }

    bool isFull() const
    {
        return size_ >= max_size_;
    }

    void push(const T &t)
    {
        std::unique_lock<std::mutex> lock(mutex_);
        is_pull_.wait(lock, [this]
                      { return this->size_ < this->max_size_; });

        array_[end_] = t;
        end_ = (end_ + 1) % max_size_;
        ++size_;
        is_empty_.notify_one();
    }

    T pop()
    {
        std::unique_lock<std::mutex> lock(mutex_);
        // is_empty_.wait(lock, [this]
        //                { return this->size_ > 0; });

        while (isEmpty())
        {
            if (is_empty_.wait_for(lock, std::chrono::milliseconds(1000)) == std::cv_status::timeout)
            {
                // 超时返回或抛出异常
                throw std::runtime_error("pop timeout");
            }
        }

        T t = array_[start_];
        start_ = (start_ + 1) % max_size_;
        --size_;
        is_pull_.notify_one();
        return t;
    }

    void stop()
    {
        is_pull_.notify_one();
        is_empty_.notify_one();
    }

    int size()
    {
        return size_;
    }

private:
    std::condition_variable is_pull_;
    std::condition_variable is_empty_;
    std::mutex mutex_;
    T *array_;
    const int max_size_;
    int start_;
    int end_;
    int size_;
};

#endif // BLOCKQUEUE_H_
